--- title: The peak pattern puzzle keywords: fastai sidebar: home_sidebar summary: "Matching peak patterns " description: "Matching peak patterns " nb_path: "notebooks/50_peak-pattern-puzzle.ipynb" ---
{% raw %}
{% endraw %}

We now arrive at the central problem of MA-XRF analysis. Given a spectrum with some peaks, and given the theoretical emission peak patterns for different chemical elements, which chemical elements are present in the sample? This is what I call the peak pattern puzzle.

It is important to note that not all peaks are always due to the actual emission of chemical elements present in the scanned object. Other peaks are generated by the instrument itself. And small peaks can also just be noise. Instrument peaks are common to all spectra. For example, the large peak observed in all spectra near zero energy is a result of the instrument detector physics. Other peaks above 18 keV are due emission and subsequent scattering of the rhodium anode present in the x-ray tube.

In order to plot an overview of emission patterns import get_patterns(), get_instrument_pattern(), plot_patterns(). To simplify our analysis let's exclude the light elements and rare elements that we will not find in drawings.

{% raw %}
from maxrf4u import get_patterns, get_instrument_pattern, plot_patterns 
{% endraw %} {% raw %}
from maxrf4u import all_elements 

'''
all_elements = ['#H', '#He', '#Li', '#Be', '#B', '#C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 
                'Al', 'Si', 'P', 'S', 'Cl', '#Ar', 'K', 'Ca', '#Sc', 'Ti', 'V', 'Cr', 
                'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', '#Ga', '#Ge', 'As', '#Se', 'Br', 
                '#Kr', '#Rb', 'Sr', '#Y', '#Zr', '#Nb', '#Mo', '#Tc', '#Ru', 'Rh', '#Pd', 
                'Ag', 'Cd', '#In', 'Sn', '#Sb', '#Te', 'I', '#Xe', '#Cs', 'Ba', '#La', '#Hf', 
                '#Ta', '#W', '#Re', '#Os', '#Ir', '#Pt', '#Au', 'Hg', '#Tl', 'Pb', '#Bi', 
                '#Po', '#At', '#Rn', '#Fr', '#Ra', '#Ac', '#Rf', '#Db', '#Sg', '#Bh', '#Hs', 
                '#Mt', '#Ds', '#Rg', '#Cn', '#Nh', '#Fl', '#Mc', '#Lv', '#Ts', '#Og']
'''

# elements of interest  
# adapt selection by (un)commenting  
elements = [elem for elem in all_elements if not '#' in elem]

print('Elements of interest:\n\n', elements)
Elements of interest:

 ['N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'K', 'Ca', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'As', 'Br', 'Sr', 'Rh', 'Ag', 'Cd', 'Sn', 'I', 'Ba', 'Hg', 'Pb']
{% endraw %} {% raw %}
elem_ptrns = get_patterns(elements) 
instrum_ptrn = get_instrument_pattern('RP-T-1898-A-3689.datastack') 

ax = plot_patterns(elem_ptrns, instrument_pattern=instrum_ptrn) 
{% endraw %}

In the previous section we have cherry picked the hotmax spectra and within each spectrum detected potentially significant peaks exceeding the Poisson noise level. We can now proceed to solve the peak pattern puzzle for each hotmax spectrum. In other words, for each spectrum explain the presence of each significant (numbered) peak. Can we attribute a given peak to a specific chemical element, the instrument or noise?

Now we can start to 'explain away' all peaks. It is highly instructive to walk through all hotmax spectra and see which element patterns explain the peak patterns that we observe. To do so, import the plot_puzzle() and plot_ptrn() functions. In the following we silently ignore the instrument peaks $\times$ on the extreme ends of the spectrum.

{% raw %}
from maxrf4u import plot_puzzle, plot_ptrn 
{% endraw %} {% raw %}
fig, ax_ptrns, ax_spectr = plot_puzzle('RP-T-1898-A-3689.datastack', 2, footspace=2)
{% endraw %} {% raw %}
plot_ptrn('Pb', -1, ax_spectr);
{% endraw %} {% raw %}
n = 0
ax0, ax1 = plot_puzzle(hma, n) 

# patterns 
plot_ptrn('Ca', -1, ax1);
{% endraw %} {% raw %}
n = 1
ax0, ax1 = plot_puzzle(hma, n) 

# patterns 
plot_ptrn('O', -1, ax1);
plot_ptrn('Ca', -1, ax1);
{% endraw %} {% raw %}
n = 2
ax0, ax1 = plot_puzzle(hma, n)


# patterns 
plot_ptrn('Pb', -1, ax1);
{% endraw %} {% raw %}
n = 3
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Cl', -1, ax1);
plot_ptrn('Ca', -1, ax1);
plot_ptrn('Fe', -2, ax1);
{% endraw %} {% raw %}
n = 4
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Fe', -3, ax1)
plot_ptrn('Ca', -1, ax1)
plot_ptrn('O', -1, ax1)
plot_ptrn('S', -1, ax1)
plot_ptrn('K', -2, ax1);
{% endraw %} {% raw %}
n = 5
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Ca', -1, ax1);
{% endraw %} {% raw %}
n = 6
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Ca', -1, ax1);
{% endraw %} {% raw %}
n = 7
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Fe', -1, ax1);
plot_ptrn('Ti', -2, ax1);
plot_ptrn('Ca', -3, ax1);
{% endraw %} {% raw %}
n = 8
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Fe', -3, ax1);
plot_ptrn('Ba', -1, ax1);
plot_ptrn('Ca', -2, ax1);
{% endraw %} {% raw %}
n = 9
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Mn', -1, ax1);
plot_ptrn('Ca', -2, ax1);
plot_ptrn('Fe', -3, ax1);
{% endraw %} {% raw %}
n = 10
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Fe', -1, ax1);
plot_ptrn('Ca', -2, ax1);
{% endraw %}

The tiny peak [6] in hotmax spectrum #10 is clearly the escape peak for Fe located at 6.40 keV minus 1.74 keV.

{% raw %}
n = 11
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Fe', -1, ax1);
plot_ptrn('Ca', -2, ax1);
{% endraw %} {% raw %}
n = 12
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Cu', -1, ax1);
plot_ptrn('Zn', -2, ax1);
plot_ptrn('Ca', -3, ax1);
{% endraw %} {% raw %}
n = 13
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Cu', -1, ax1);
plot_ptrn('Zn', -2, ax1);
plot_ptrn('Ca', -3, ax1);
{% endraw %} {% raw %}
n = 14
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Cu', -1, ax1);
plot_ptrn('Zn', -2, ax1);
plot_ptrn('Ca', -3, ax1);
{% endraw %} {% raw %}
n = 15
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Pb', -1, ax1);
{% endraw %} {% raw %}
n = 16
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Pb', -1, ax1);
{% endraw %} {% raw %}
n = 17
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Ca', -1, ax1);
{% endraw %} {% raw %}
n = 18
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Pb', -1, ax1);
{% endraw %} {% raw %}
n = 19
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Ca', -1, ax1);
{% endraw %} {% raw %}
n = 20
ax0, ax1 = plot_puzzle(hma, n)

# patterns 
plot_ptrn('Pb', -1, ax1);
{% endraw %}

Ok, that is it. Let's try to summarize what we have learned...

Summary

{% raw %}
from maxrf4u import plot_patterns, get_patterns, DataStack 
from maxrf4u.peakmaps import _add_hotlines_ticklabels

ds = DataStack('RP-T-1898-A-3689.datastack')

x_keVs = ds.read('maxrf_energies') 
y_max = ds.read('maxrf_maxspectrum')
y_sum = ds.read('maxrf_sumspectrum') 
hotmax_spectra = ds.read('hotmax_spectra')

ptrn_list = get_patterns(['S', 'Ca', 'K', 'Cl', 'Fe', 'Mn', 'Cu', 'Zn', 'Pb', 'Ti', 'Ba'])

fig, [ax, ax1] = plt.subplots(nrows=2, sharex=True, figsize=[9, 5])

plot_patterns(ptrn_list, ax=ax)
_add_hotlines_ticklabels('RP-T-1898-A-3689.datastack', ax, clip_vline=False) 

#plot_cube_slices('RP-T-1898-A-3689.datastack', ax=ax1); 

ax1.plot(x_keVs, y_max, color='r', label='max spectrum') 
ax1.fill_between(x_keVs, y_max, color='r', alpha=0.3)

for y_hot in hotmax_spectra: 
    ax1.plot(x_keVs, y_hot, color=[0.2, 0.1, 0.8], linewidth=0.5) 
#ax1.plot(x_keVs, y_sum, color=[0.3, 1, 0.3], label='sum spectrum')
_add_hotlines_ticklabels('RP-T-1898-A-3689.datastack', ax1) 

ax1.set_xlim([-1, 23])
ax1.set_ylim([-5, 100])
ax1.legend();

ax.set_title('Peak pattern summary')
plt.tight_layout()
{% endraw %}

Altogether the spectral data indicates that 11 chemical elements are present in the Susanna drawing: sulfur (S), chlorine (Cl), potassium (K), calcium (Ca), barium (Ba), titanium (Ti), manganese (Mn), iron (Fe), copper (Cu), zinc (Zn) and lead (Pb).

In the next section we will look into the spatial distribution of these elements...

Functions

{% raw %}

get_patterns[source]

get_patterns(elements, tube_keV=30, eoi=None)

Returns sorted pattern dict list for `elements` list, according to alpha peak energy.
{% endraw %} {% raw %}

colorize[source]

colorize(elem, eoi=None)

Pick fixed color from nice color map for elements of interest.
{% endraw %} {% raw %}

plot_ptrn[source]

plot_ptrn(elem, y, ax, eoi=None, escape=True)

Low level plot element pattern at level `y` in axes `ax`.
{% endraw %} {% raw %}

plot_patterns[source]

plot_patterns(ptrn_list, instrument_pattern=None, datastack_file=None, ax=None, eoi=None, escape=True)

Plot overview of instrument and element patterns `ptrn_list` in axes `ax`

Returns: `ax`
{% endraw %} {% raw %}

plot_puzzle[source]

plot_puzzle(datastack_file, n, elements=None, footspace=0.2)

Plot peak pattern puzzle with patterns and spectrum
{% endraw %} {% raw %}

get_instrument_pattern[source]

get_instrument_pattern(datastack_file)

Generate instrument peak pattern.

Pattern dictionary contains strongest Rhodium anode emission peaks,
their corresponding Compton shifted peaks, and a sensor peak

Returns: instrument_pattern_dict
{% endraw %} {% raw %}

add_hotlines[source]

add_hotlines(datastack_file, ax, vlines=True, clip_vline=True)

Utility function. Adds hotlines and tick labels to plot `ax`.

Instrument peaks are colored black.
{% endraw %} {% raw %}
{% endraw %}